#include <status.h>
#include <device/device.h>
#include <log.h>
#include <fs/devfs.h>

#include <mm/valloc.h>

static lnode_t dev_list = { .next = &dev_list, .prev = &dev_list };
static volatile u32 dev_id = 0;

device_t* device_add(device_t* parent, void* impl, char* name, u32 name_len, u32 type) {
    device_t* dev = vzalloc(sizeof(device_t));

    if (parent) {
        list_append(&parent->children, &dev->siblings);
    } else {
        list_append(&dev_list, &dev->siblings);
    }

    strncpy(dev->name_buf, name, DEV_NAME_LEN);

    dev->name = HSTR(dev->name_buf, name_len);
    dev->parent = parent;
    dev->impl = impl;
    dev->type = type;
    dev->id = dev_id++;

    hstr_rehash(&dev->name);
    list_head_init(&dev->children);
    return dev;
}

void device_remove(device_t* dev) {
    list_remove(&dev->siblings);
    vfree(dev);
}

device_t* device_getbyid(lnode_t* devlist, u32 id) {
    devlist = devlist ? devlist : &dev_list;
    device_t* pos, * n;
    list_for_each(pos, n, devlist, siblings) {
        if (pos->id == id) {
            return pos;
        }
    }

    return NULL;
}

device_t* device_getbyhname(device_t* root_dev, hstr_t* name) {
    lnode_t* devlist = root_dev ? &root_dev->children : &dev_list;
    device_t* pos, * n;
    list_for_each(pos, n, devlist, siblings) {
        if (HSTR_EQ(&pos->name, name)) {
            return pos;
        }
    }

    return NULL;
}

device_t* device_getbyoffset(device_t* root_dev, size_t offset) {
    lnode_t* devlist = root_dev ? &root_dev->children : &dev_list;
    device_t* pos, * n;
    list_for_each(pos, n, devlist, siblings) {
        if (offset-- == 0) {
            return pos;
        }
    }

    return NULL;
}